package com.msun.auth.apiAdmin.handler;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.msun.auth.apiAdmin.AdminContext;
import com.msun.auth.custom.annotation.ContextWithToken;
import com.msun.auth.custom.annotation.ResponseResult;
import com.msun.auth.custom.annotation.WithoutToken;
import com.msun.auth.custom.comdto.DataBox;
import com.msun.auth.custom.comdto.PageListDto;
import com.msun.auth.custom.constants.RoleLevels;
import com.msun.auth.custom.exception.BizException;
import com.msun.auth.custom.exception.BizResultCode;
import com.msun.auth.database.model.*;
import com.msun.auth.utils.CheckUtils;
import com.msun.auth.utils.id.IdGenerator;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * 类描述：
 *
 * @ClassName GroupManager
 * @Description TODO
 * @Author gj
 * @Date 2023/8/19 12:55
 * @Version 1.0
 */

@Api(tags = "小组管理")
@ResponseResult
@ContextWithToken
@RestController
@RequestMapping("/admin/group/")
public class GroupManager {
    @Autowired
    private GroupMapper groupMapper;
    @Autowired
    private GroupRoleMapper groupRoleMapper;
    @Autowired
    private GroupUserMapper groupUserMapper;
    @Autowired
    private AppMapper appMapper;
    @Autowired
    private AppUserMapper appUserMapper;

    @Data
    public static class CreateGroupArgs {
        private String groupName;
        private Long adminUid;
    }

    @ApiOperation("超级管理员-创建小组")
    @ApiResponses({@ApiResponse(code = 200, message = "成功", response = GroupMo.class)})
    @Transactional(rollbackFor = Exception.class)
    @PostMapping("superAdminCreateGroup")
    public GroupMo superAdminCreateGroup(@RequestBody CreateGroupArgs args) {
        AdminContext.Context ctx = AdminContext.get();
        if (ctx.getRoleLevel() < RoleLevels.SUPER_ADMIN) {
            throw new BizException(BizResultCode.NoRight);
        }
        if (new LambdaQueryChainWrapper<>(groupMapper)
                .eq(GroupMo::getGroupName, args.getGroupName())
                .exists()) {
            throw new BizException(BizResultCode.Error, StrUtil.format("已经存在小组【{}】", args.getGroupName()));
        }
        GroupMo group = new GroupMo();
        group.setId(IdGenerator.ins().generator());
        group.setGroupName(args.getGroupName());
        group.setCreateTime(new Date());
        groupMapper.insert(group);
        GroupRoleMo adminRole = new GroupRoleMo();
        adminRole.setId(IdGenerator.ins().generator());
        adminRole.setGroupId(group.getId());
        adminRole.setRoleName("小组管理员");
        adminRole.setRoleLevel(RoleLevels.GROUP_ADMIN);
        GroupRoleMo appRole = new GroupRoleMo();
        appRole.setId(IdGenerator.ins().generator());
        appRole.setGroupId(group.getId());
        appRole.setRoleName("应用管理员");
        appRole.setRoleLevel(RoleLevels.APP_ADMIN);
        groupRoleMapper.insertBatch(Arrays.asList(adminRole, appRole));
        GroupUserMo groupUser = new GroupUserMo();
        groupUser.setId(IdGenerator.ins().generator());
        groupUser.setGroupId(group.getId());
        groupUser.setUid(args.getAdminUid());
        groupUser.setGroupRoleId(adminRole.getId());
        groupUser.setCreateTime(new Date());
        groupUserMapper.insert(groupUser);
        return group;
    }

    @Data
    public static class DelGroupArgs {
        private Long groupId;
    }

    @ApiOperation("超级管理员-删除小组")
    @ApiResponses({@ApiResponse(code = 200, message = "成功", response = DataBox.class)})
    @Transactional(rollbackFor = Exception.class)
    @PostMapping("superAdminDelGroup")
    public DataBox superAdminDelGroup(@RequestBody DelGroupArgs args) {
        AdminContext.Context ctx = AdminContext.get();
        if (ctx.getRoleLevel() < RoleLevels.SUPER_ADMIN) {
            throw new BizException(BizResultCode.NoRight);
        }
        Long count = new LambdaQueryChainWrapper<>(groupRoleMapper)
                .eq(GroupRoleMo::getGroupId, args.getGroupId())
                .count();
        if (!CheckUtils.isZero(count)) {
            throw new BizException(BizResultCode.Error, "请先删除小组角色");
        }
        count = new LambdaQueryChainWrapper<>(groupUserMapper)
                .eq(GroupUserMo::getGroupId, args.getGroupId())
                .count();
        if (!CheckUtils.isZero(count)) {
            throw new BizException(BizResultCode.Error, "请先删除小组用户");
        }
        count = new LambdaQueryChainWrapper<>(appMapper)
                .eq(AppMo::getGroupId, args.getGroupId())
                .count();
        if (!CheckUtils.isZero(count)) {
            throw new BizException(BizResultCode.Error, "请先删除小组下的所有应用");
        }
        groupMapper.deleteById(args.getGroupId());
        return DataBox.of(true);
    }

    @Data
    public static class EditGroupNameArgs {
        private Long groupId;
        private String groupName;
    }

    @ApiOperation("超级管理员-编辑小组名称")
    @ApiResponses({@ApiResponse(code = 200, message = "成功", response = GroupMo.class)})
    @Transactional(rollbackFor = Exception.class)
    @PostMapping("superAdminEditGroupName")
    public GroupMo superAdminEditGroupName(@RequestBody EditGroupNameArgs args) {
        AdminContext.Context ctx = AdminContext.get();
        if (ctx.getRoleLevel() < RoleLevels.SUPER_ADMIN) {
            throw new BizException(BizResultCode.NoRight);
        }
        if (new LambdaQueryChainWrapper<>(groupMapper)
                .eq(GroupMo::getGroupName, args.getGroupName())
                .exists()) {
            throw new BizException(BizResultCode.Error, StrUtil.format("已经存在小组【{}】", args.getGroupName()));
        }
        GroupMo group = new LambdaQueryChainWrapper<>(groupMapper)
                .eq(GroupMo::getId, args.getGroupId())
                .one();
        if (group == null) {
            throw new BizException(BizResultCode.RecordNotExist);
        }
        group.setGroupName(args.getGroupName());
        groupMapper.updateById(group);
        return group;
    }

    @Data
    public static class EditGroupRoleArgs {
        @NotBlank
        private String roleName;
        @NotNull
        private Integer roleLevel;
    }

    @ApiOperation("小组管理员-编辑小组角色")
    @ApiResponses({@ApiResponse(code = 200, message = "成功", response = GroupRoleMo.class)})
    @Transactional(rollbackFor = Exception.class)
    @PostMapping("groupAdminEditGroupRole")
    public GroupRoleMo groupAdminEditGroupRole(@RequestBody @Valid EditGroupRoleArgs args) {
        AdminContext.Context ctx = AdminContext.get();
        if (ctx.getRoleLevel() < RoleLevels.GROUP_ADMIN) {
            throw new BizException(BizResultCode.NoRight);
        }
        if (new LambdaQueryChainWrapper<>(groupRoleMapper)
                .eq(GroupRoleMo::getGroupId, ctx.getGroupId())
                .eq(GroupRoleMo::getRoleName, args.getRoleName())
                .exists()) {
            throw new BizException(BizResultCode.Error, "同一个小组内不允许存在同名角色");
        }
        if (args.getRoleLevel() >= RoleLevels.APP_ADMIN) {
            if (new LambdaQueryChainWrapper<>(groupRoleMapper)
                    .eq(GroupRoleMo::getGroupId, ctx.getGroupId())
                    .eq(GroupRoleMo::getRoleLevel, args.getRoleLevel()).exists()) {
                throw new BizException(BizResultCode.Error, StrUtil.format("{}管理员角色已经存在，不允许同时存在多个该管理员角色", args.getRoleLevel() == RoleLevels.APP_ADMIN ? "应用" : "小组"));
            }
        }
        GroupRoleMo groupRole = new LambdaQueryChainWrapper<>(groupRoleMapper)
                .eq(GroupRoleMo::getGroupId, ctx.getGroupId())
                .eq(GroupRoleMo::getRoleName, args.getRoleName())
                .one();
        boolean create = false;
        if (groupRole == null) {
            create = true;
            groupRole = new GroupRoleMo();
            groupRole.setId(IdGenerator.ins().generator());
        }
        groupRole.setGroupId(ctx.getGroupId());
        groupRole.setRoleName(args.getRoleName());
        groupRole.setRoleLevel(args.getRoleLevel());
        if (create) {
            groupRoleMapper.insert(groupRole);

        } else {
            groupRoleMapper.updateById(groupRole);
        }
        return groupRole;
    }

    @Data
    public static class DelGroupRoleArgs {
        private Long groupRoleId;
    }

    @ApiOperation("小组管理员-删除小组角色")
    @ApiResponses({@ApiResponse(code = 200, message = "成功", response = DataBox.class)})
    @Transactional(rollbackFor = Exception.class)
    @PostMapping("groupAdminDelGroupRole")
    public DataBox groupAdminDelGroupRole(@RequestBody DelGroupRoleArgs args) {
        AdminContext.Context ctx = AdminContext.get();
        if (ctx.getRoleLevel() < RoleLevels.GROUP_ADMIN) {
            throw new BizException(BizResultCode.NoRight);
        }
        GroupRoleMo groupRole = new LambdaQueryChainWrapper<>(groupRoleMapper)
                .eq(GroupRoleMo::getId, args.getGroupRoleId())
                .one();
        if (groupRole == null) {
            return DataBox.of(true);
        }
        if (!Objects.equals(groupRole.getGroupId(), ctx.getGroupId())) {
            throw new BizException(BizResultCode.NoRight);
        }
        if (new LambdaQueryChainWrapper<>(appUserMapper)
                .eq(AppUserMo::getGroupRoleId, args.getGroupRoleId())
                .exists()) {
            throw new BizException(BizResultCode.Error, "部分应用的用户已经被分配了该角色，请先替换成其他角色后才可删除");
        }
        groupRoleMapper.deleteById(args.getGroupRoleId());
        return DataBox.of(true);
    }

    @Data
    public static class AuthGroupAdminArgs {
        private Long uid;
        private Long groupId;
    }

    @ApiOperation("超级管理员-给用户授权为小组管理员")
    @ApiResponses({@ApiResponse(code = 200, message = "成功", response = GroupUserMo.class)})
    @Transactional(rollbackFor = Exception.class)
    @PostMapping("superAdminAuthGroupAdmin")
    public GroupUserMo superAdminAuthGroupAdmin(@RequestBody AuthGroupAdminArgs args) {
        AdminContext.Context ctx = AdminContext.get();
        if (ctx.getRoleLevel() < RoleLevels.SUPER_ADMIN) {
            throw new BizException(BizResultCode.NoRight);
        }
        GroupRoleMo groupRole = new LambdaQueryChainWrapper<>(groupRoleMapper)
                .eq(GroupRoleMo::getGroupId, args.getGroupId())
                .eq(GroupRoleMo::getRoleLevel, RoleLevels.GROUP_ADMIN)
                .one();
        if (groupRole == null) {
            throw new BizException(BizResultCode.RecordNotExist, "该小组的管理员角色已被删除");
        }
        GroupUserMo groupUser = new GroupUserMo();
        groupUser.setId(IdGenerator.ins().generator());
        groupUser.setGroupId(args.getGroupId());
        groupUser.setUid(args.getUid());
        groupUser.setGroupRoleId(groupRole.getId());
        groupUser.setCreateTime(new Date());
        groupUserMapper.insert(groupUser);
        return groupUser;
    }

    @Data
    public static class AddGroupUserArgs {
        private Long uid;
        private Long groupRoleId;
    }

    @ApiOperation("小组管理员-编辑小组用户")
    @ApiResponses({@ApiResponse(code = 200, message = "成功", response = GroupUserMo.class)})
    @Transactional(rollbackFor = Exception.class)
    @PostMapping("groupAdminEditGroupUser")
    public GroupUserMo groupAdminEditGroupUser(@RequestBody AddGroupUserArgs args) {
        AdminContext.Context ctx = AdminContext.get();
        if (ctx.getRoleLevel() < RoleLevels.GROUP_ADMIN) {
            throw new BizException(BizResultCode.NoRight);
        }
        GroupUserMo groupUser = new LambdaQueryChainWrapper<>(groupUserMapper)
                .eq(GroupUserMo::getGroupId, ctx.getGroupId())
                .eq(GroupUserMo::getUid, args.getUid())
                .one();
        boolean create = false;
        if (groupUser == null) {
            create = true;
            groupUser = new GroupUserMo();
            groupUser.setId(IdGenerator.ins().generator());
        }
        groupUser.setGroupId(ctx.getGroupId());
        groupUser.setUid(args.getUid());
        groupUser.setGroupRoleId(args.getGroupRoleId());
        groupUser.setCreateTime(new Date());
        if (create) {
            groupUserMapper.insert(groupUser);
        } else {
            groupUserMapper.updateById(groupUser);
        }
        return groupUser;
    }

    @Data
    public static class DelGroupUserArgs {
        private Long uid;
    }

    @ApiOperation("小组管理员-删除小组用户")
    @ApiResponses({@ApiResponse(code = 200, message = "成功", response = DataBox.class)})
    @Transactional(rollbackFor = Exception.class)
    @PostMapping("groupAdminDelGroupUser")
    public DataBox groupAdminDelGroupUser(@RequestBody DelGroupUserArgs args) {
        AdminContext.Context ctx = AdminContext.get();
        if (ctx.getRoleLevel() < RoleLevels.GROUP_ADMIN) {
            throw new BizException(BizResultCode.NoRight);
        }
        if (new LambdaQueryChainWrapper<>(appUserMapper)
                .eq(AppUserMo::getGroupId, ctx.getGroupId())
                .exists()) {
            throw new BizException(BizResultCode.Error, "请先从所有应用成员中移除该用户");
        }
        GroupUserMo groupUser = new LambdaQueryChainWrapper<>(groupUserMapper)
                .eq(GroupUserMo::getGroupId, ctx.getGroupId())
                .eq(GroupUserMo::getUid, args.getUid())
                .one();
        if (groupUser == null) {
            return DataBox.of(true);
        }
        groupUserMapper.deleteById(args.getUid());
        return DataBox.of(true);
    }

    @Data
    public static class GetGroupListArgs {
        public Boolean all;
        private Integer pageNo;
        private Integer pageSize;
        private String keyword;
    }

    //获取小组列表
    @ApiOperation("登录页面and超级管理员-获取小组列表")
    @ApiResponses({@ApiResponse(code = 200, message = "成功", response = GroupMo.class)})
    @WithoutToken
    @PostMapping("getGroupList")
    public PageListDto<GroupMo> getGroupList(@RequestBody GetGroupListArgs args) {
        PageListDto<GroupMo> dto = new PageListDto<>();
        List<GroupMo> list;
        if (Boolean.TRUE.equals(args.getAll())) {
            list = new LambdaQueryChainWrapper<>(groupMapper)
                    .gt(GroupMo::getId, 0)
                    .like(StrUtil.isNotBlank(args.getKeyword()), GroupMo::getGroupName, args.getKeyword())
                    .orderByAsc(GroupMo::getId).list();
        } else {
            if (CheckUtils.isZero(args.getPageNo())) {
                args.setPageNo(1);
            }
            if (CheckUtils.isZero(args.getPageSize())) {
                args.setPageSize(20);
            }
            Page<GroupMo> page = new LambdaQueryChainWrapper<>(groupMapper)
                    .gt(GroupMo::getId, 0)
                    .like(StrUtil.isNotBlank(args.getKeyword()), GroupMo::getGroupName, args.getKeyword())
                    .orderByAsc(GroupMo::getId)
                    .page(new Page<>(args.getPageNo(), args.getPageSize(), true));
            list = page.getRecords();
            dto.setPageNo(args.getPageNo());
            dto.setPageSize(args.getPageSize());
            dto.setTotal(page.getTotal());
        }
        dto.setArr(list);
        return dto;
    }

    @Data
    public static class GetGroupAndAppListArgs {
        public Boolean all;
        private Integer pageNo;
        private Integer pageSize;
        private String keyword;
    }

    @ApiOperation("登录页面-获取小组以及应用列表")
    @ApiResponses({@ApiResponse(code = 200, message = "成功", response = GroupMo.class)})
    @WithoutToken
    @PostMapping("getGroupAndAppList")
    public PageListDto<GroupMo> getGroupAndAppList(@RequestBody GetGroupAndAppListArgs args) {
        PageListDto<GroupMo> dto = new PageListDto<>();
        List<GroupMo> list;
        if (Boolean.TRUE.equals(args.getAll())) {
            list = new LambdaQueryChainWrapper<>(groupMapper)
                    .gt(GroupMo::getId, 0)
                    .like(StrUtil.isNotBlank(args.getKeyword()), GroupMo::getGroupName, args.getKeyword())
                    .orderByAsc(GroupMo::getId).list();
        } else {
            if (CheckUtils.isZero(args.getPageNo())) {
                args.setPageNo(1);
            }
            if (CheckUtils.isZero(args.getPageSize())) {
                args.setPageSize(20);
            }
            Page<GroupMo> page = new LambdaQueryChainWrapper<>(groupMapper)
                    .gt(GroupMo::getId, 0)
                    .like(StrUtil.isNotBlank(args.getKeyword()), GroupMo::getGroupName, args.getKeyword())
                    .orderByAsc(GroupMo::getId)
                    .page(new Page<>(args.getPageNo(), args.getPageSize(), true));
            list = page.getRecords();
            dto.setPageNo(args.getPageNo());
            dto.setPageSize(args.getPageSize());
            dto.setTotal(page.getTotal());
        }
        for (GroupMo group : list) {
            List<AppMo> apps = new LambdaQueryChainWrapper<>(appMapper)
                    .eq(AppMo::getGroupId, group.getId())
                    .orderByAsc(AppMo::getId)
                    .list();
            group.setAppList(apps);
        }
        dto.setArr(list);
        return dto;
    }

    @Data
    public static class GetGroupUserListArgs {
        private Integer pageNo;
        private Integer pageSize;
        private Long groupRoleId;
        private String keyword;
    }

    @ApiOperation("小组and应用管理员-获取小组成员列表")
    @ApiResponses({@ApiResponse(code = 200, message = "成功", response = UserMo.class)})
    @PostMapping("groupAdminGetGroupUserList")
    public PageListDto<UserMo> groupAdminGetGroupUserList(@RequestBody GetGroupUserListArgs args) {
        AdminContext.Context ctx = AdminContext.get();
        if (ctx.getRoleLevel() < RoleLevels.APP_ADMIN) {
            throw new BizException(BizResultCode.NoRight);
        }
        if (CheckUtils.isZero(args.getPageNo())) {
            args.setPageNo(1);
        }
        if (CheckUtils.isZero(args.getPageSize())) {
            args.setPageSize(20);
        }
        String keyword = null;
        if (StrUtil.isNotBlank(args.getKeyword())) {
            keyword = StrUtil.format("%{}%", args.getKeyword());
        }
        List<UserMo> users = groupUserMapper.getUserList(ctx.getGroupId(), (args.getPageNo() - 1) * args.getPageSize(), args.getPageSize(), args.getGroupRoleId(), keyword);
        Integer count = groupUserMapper.getUserCount(ctx.getGroupId(), args.getGroupRoleId(), keyword);
        PageListDto<UserMo> dto = new PageListDto<>();
        dto.setArr(users);
        dto.setPageNo(args.getPageNo());
        dto.setPageSize(args.getPageSize());
        dto.setTotal(count);
        return dto;
    }

    @Data
    public static class GetGroupRoleListArgs {
        private Integer pageNo;
        private Integer pageSize;
        private String keyword;
    }

    @ApiOperation("小组and应用管理员-获取小组角色列表")
    @ApiResponses({@ApiResponse(code = 200, message = "成功", response = GroupRoleMo.class)})
    @PostMapping("groupAdminGetGroupRoleList")
    public PageListDto<GroupRoleMo> groupAdminGetGroupRoleList(@RequestBody GetGroupRoleListArgs args) {
        AdminContext.Context ctx = AdminContext.get();
        if (ctx.getRoleLevel() < RoleLevels.APP_ADMIN) {
            throw new BizException(BizResultCode.NoRight);
        }
        if (CheckUtils.isZero(args.getPageNo())) {
            args.setPageNo(1);
        }
        if (CheckUtils.isZero(args.getPageSize())) {
            args.setPageSize(20);
        }
        Page<GroupRoleMo> page = new LambdaQueryChainWrapper<>(groupRoleMapper)
                .eq(GroupRoleMo::getGroupId, ctx.getGroupId())
                .like(StrUtil.isNotBlank(args.getKeyword()), GroupRoleMo::getRoleName, args.getKeyword())
                .orderByAsc(GroupRoleMo::getId)
                .page(new Page<>(args.getPageNo(), args.getPageSize(), true));
        PageListDto<GroupRoleMo> dto = new PageListDto<>();
        dto.setArr(page.getRecords());
        dto.setPageNo(args.getPageNo());
        dto.setPageSize(args.getPageSize());
        dto.setTotal(page.getTotal());
        return dto;
    }


}